/* MQTT (over TCP) Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_wifi.h"
// #include "esp_system.h"
// #include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
// #include "protocol_examples_common.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "freertos/event_groups.h"

// #include "lwip/sockets.h"
// #include "lwip/dns.h"
// #include "lwip/netdb.h"

#include "esp_log.h"
#include "mbedtls/md.h"
#include "mbedtls/base64.h"
#include "mqtt_event.h"
#include "main.h"
#include "radar.h"
#include "mqtt_cjson_handle.h"
#include "station.h"
#include "nvs_value.h"
#include "ws2812.h"
#include "h03_config.h"

static const char *TAG = "[MQTT]";

static uint16_t mqtt_disconnect_count = 0;
esp_mqtt_client_handle_t client;

bool wifi_stop_flag = 0;

char MQTT_TOPIC_ATTR[50];
char MQTT_TOPIC_FUNC[50];
char MQTT_TOPIC_EVENT[50];
char MQTT_TOPIC_INFO[50];
char MQTT_TOPIC_CTRL[50];
char MQTT_TOPIC_REPORT[50];
char MQTT_TOPIC_DEBUG[50];
char MQTT_TOPIC_temp[50];

static void log_error_if_nonzero(const char *message, int error_code)
{
    if (error_code != 0) {
        ESP_LOGE(TAG, "Last error %s: 0x%x", message, error_code);
    }
}

/*
 * @brief Event handler registered to receive MQTT events
 *
 *  This function is called by the MQTT client event loop.
 *
 * @param handler_args user data registered to the event.
 * @param base Event base for the handler(always MQTT Base in this example).
 * @param event_id The id for the received event.
 * @param event_data The data for the event, esp_mqtt_event_handle_t.
 */
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    static uint8_t system_init = 0;    

    ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%ld", base, event_id);
    esp_mqtt_event_handle_t event = event_data;
    int32_t msg_id;
    switch ((esp_mqtt_event_id_t)event_id) {
        case MQTT_EVENT_CONNECTED:
            ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
            msg_id = esp_mqtt_client_subscribe(client, MQTT_TOPIC_FUNC, 0);
            ESP_LOGI(TAG, "sent func subscribe successful, msg_id=%ld", msg_id);
            if(msg_id == -1){
                ESP_LOGI(TAG, "MQTT_EVENT_RESET! to mqtt_cjson_func");
                restart_os();
            }
            msg_id = esp_mqtt_client_subscribe(client, MQTT_TOPIC_CTRL, 0);
            ESP_LOGI(TAG, "sent ctrl subscribe successful, msg_id=%ld", msg_id);
            if(msg_id == -1){
                ESP_LOGI(TAG, "MQTT_EVENT_RESET! to mqtt_cjson_ctrl");
                restart_os();
            }
            nvs_record_error(0, 10);
            cJSON_creat_online();
            mqtt_disconnect_count = 0;
            event_flag.time_sync = 1;
            system_state.main = NORMAL;
            event_flag.mqtt_heartbeat = 1;
            event_flag.all_attr = 1;
            if(system_init == 0){
                system_init = 1;
                paramenter_setting_u8("s_ota", 0);
                if(top_led_mode == 1){
                    top_led_mode = 2;
                    event_flag.led_break = 1;
                }
            }
            break;
        case MQTT_EVENT_DISCONNECTED:
            ESP_LOGE(TAG, "MQTT_EVENT_DISCONNECTED");
            // if(wifi_stop_flag == 1 && system_state.main != BOOTING){
                system_state.main = MQTT_DISCONNECTED;
                event_flag.led_break = 1;
            // }
            if(system_state.wifi == wifi_SUCCESS){
                mqtt_disconnect_count++;
                if(device_attr.mqtt_param.mqttwork == 0){
                    paramenter_setting_u8("newuser", 0);
                    paramenter_setting_u8("mqttwork", 1);
                    led_notify = RED_NOTIFICATION;
                    event_flag.led_break = 1;
                    ESP_LOGE(TAG, "unable to connect MQTT,rolling back");
                    restart_os();
                }
            }else{
                ESP_LOGI(TAG, "mqtt disconnect");
                esp_mqtt_client_disconnect(client);
            }
		    if(mqtt_disconnect_count > 3){
                mqtt_disconnect_count = 0;
                wifi_stop_flag = 1;
                ESP_ERROR_CHECK(esp_wifi_disconnect());
                vTaskDelay(1000 / portTICK_PERIOD_MS);
                ESP_ERROR_CHECK(esp_wifi_stop());
                vTaskDelay(200 / portTICK_PERIOD_MS);
                ESP_ERROR_CHECK(esp_wifi_start());
                wifi_stop_flag = 0;
                ESP_LOGI(TAG, "mqtt reconnect");
                nvs_record_error(1, 10);
		    }

            break;
        case MQTT_EVENT_SUBSCRIBED:
            ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
            if(device_attr.mqtt_param.mqttwork == 0){
                device_attr.mqtt_param.mqttwork = 1;
                paramenter_setting_u8("mqttwork", 1);
                led_notify = BLUE_NOTIFICATION;
                event_flag.led_break = 1;
                ESP_LOGI(TAG, "new broken work success");
            }
            break;
        case MQTT_EVENT_UNSUBSCRIBED:
            ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
            break;
        case MQTT_EVENT_PUBLISHED:
            ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
            break;
        case MQTT_EVENT_DATA:
            ESP_LOGI(TAG, "MQTT_EVENT_DATA");
            if(device_attr.debug_mode == 2){
                printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
                printf("DATA=%.*s\r\n", event->data_len, event->data);
            }
            mqtt_data_parse(event->topic, event->data);
            break;
        case MQTT_EVENT_ERROR:
            ESP_LOGE(TAG, "MQTT_EVENT_ERROR");
            if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) {
                log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err);
                log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err);
                log_error_if_nonzero("captured as transport's socket errno",  event->error_handle->esp_transport_sock_errno);
                ESP_LOGE(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno));
            }
            break;
        default:
            ESP_LOGI(TAG, "Other event id:%d", event->event_id);
            break;
    }
}

void mqtt_reconnect(void)
{
    esp_mqtt_client_reconnect(client);
}

void mqtt_app_start(void)
{
    char mqtt_client_id[47] = {0};

    esp_mqtt_client_config_t mqtt_cfg = {
        .broker.address.uri = device_attr.mqtt_param.mqtt_broken_url,
		.broker.address.port = 1883,
        .session.keepalive = 60,
        .session.protocol_ver = device_attr.mqtt_param.mqttver,
    };
    ESP_LOGD(TAG, "mqtt_broken_url: %s", mqtt_cfg.broker.address.uri);
    ESP_LOGD(TAG, "mqtt ver: %d", mqtt_cfg.session.protocol_ver);

    if(device_attr.mqtt_param.decode == 1 && strstr(device_attr.mqtt_param.mqtt_broken_url,"mqtt://") != NULL){
        char aliyun_username[128];
        char aliyun_password[64];
        char aliyun_devicesn[64];
        unsigned char hmac_sha1_output[20] = {0};
        size_t base64_len;
        const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);

        ESP_LOGI(TAG,"aliyun MQTT mode!");
                
        char* start = strstr(device_attr.mqtt_param.mqtt_broken_url, "mqtt://");
        char* end = strchr(start, '.');
        int length = end - start - strlen("mqtt://");
        char Instance_ID[length + 1];
        strncpy(Instance_ID, start + strlen("mqtt://"), length);
        Instance_ID[length] = '\0';

        sprintf(aliyun_devicesn, "%s@@@%s", device_attr.mqtt_param.user_groupid, device_attr.device_mac);
        ESP_LOGI(TAG, "aliyun_devicesn: %s", aliyun_devicesn);

        sprintf(aliyun_username,"Signature|%s|%s",device_attr.mqtt_param.mqtt_broken_accesskey, "mqtt-cn-ot93vfkv202");
        ESP_LOGI(TAG,"aliyun_username: %s",aliyun_username);

        mbedtls_md_hmac(md_info, (const unsigned char *) device_attr.mqtt_param.mqtt_broken_secretkey, strlen(device_attr.mqtt_param.mqtt_broken_secretkey), (const unsigned char *) aliyun_devicesn, strlen(aliyun_devicesn), hmac_sha1_output);
        mbedtls_base64_encode((unsigned char *) aliyun_password, sizeof(aliyun_password), &base64_len, hmac_sha1_output, sizeof(hmac_sha1_output));
        aliyun_password[base64_len] = '\0';
        ESP_LOGI(TAG, "aliyun_password: %s", aliyun_password);

        mqtt_cfg.credentials.username = aliyun_username;
        mqtt_cfg.credentials.authentication.password = aliyun_password;
        mqtt_cfg.credentials.client_id = aliyun_devicesn;
    }else{
        ESP_LOGI(TAG,"normal MQTT mode!");

        sprintf(mqtt_client_id, "GID_HJY_MQTTX@@@%s", device_attr.device_mac);

        mqtt_cfg.credentials.client_id = mqtt_client_id;
        ESP_LOGD(TAG, "mqtt_client_id: %s", mqtt_cfg.credentials.client_id);
        mqtt_cfg.credentials.username = device_attr.mqtt_param.mqtt_broken_accesskey;
        ESP_LOGD(TAG,"mqtt_broken_accesskey: %s",mqtt_cfg.credentials.username);
        mqtt_cfg.credentials.authentication.password = device_attr.mqtt_param.mqtt_broken_secretkey;
        ESP_LOGD(TAG, "mqtt_broken_secretkey: %s", mqtt_cfg.credentials.authentication.password);
    }

#if CONFIG_BROKER_URL_FROM_STDIN
    char line[128];

    if (strcmp(mqtt_cfg.broker.address.uri, "FROM_STDIN") == 0) {
        int count = 0;
        printf("Please enter url of mqtt broker\n");
        while (count < 128) {
            int c = fgetc(stdin);
            if (c == '\n') {
                line[count] = '\0';
                break;
            } else if (c > 0 && c < 127) {
                line[count] = c;
                ++count;
            }
            vTaskDelay(10 / portTICK_PERIOD_MS);
        }
        mqtt_cfg.broker.address.uri = line;
        ESP_LOGI(TAG, "Broker url: %s", line);
    } else {
        ESP_LOGE(TAG, "Configuration mismatch: wrong broker url");
        abort();
    }
#endif /* CONFIG_BROKER_URL_FROM_STDIN */

    client = esp_mqtt_client_init(&mqtt_cfg);
    if(client == NULL){
    	ESP_LOGE(TAG,"client is null");
        if(device_attr.mqtt_param.mqttwork == 0){
            paramenter_setting_u8("newuser", 0);
            paramenter_setting_u8("mqttwork", 1);
            ESP_LOGE(TAG, "unable to connect MQTT,rolling back");
            restart_os();
        }
    }
    ESP_LOGI(TAG,"mqtt_client_start");
    /* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */
    esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
    esp_mqtt_client_start(client);
}

void mqtt_task(void)
{
    ESP_LOGI(TAG,"start MQTT");

    system_state.main = STATION;
    event_flag.led_break = 1;
    
	ESP_LOGI(TAG, "[APP] Startup..");
    ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());

    // esp_log_level_set("*", ESP_LOG_INFO);
    // esp_log_level_set("mqtt_client", ESP_LOG_VERBOSE);
    // esp_log_level_set("MQTT_EXAMPLE", ESP_LOG_VERBOSE);
    // esp_log_level_set("TRANSPORT_BASE", ESP_LOG_VERBOSE);
    // esp_log_level_set("esp-tls", ESP_LOG_VERBOSE);
    // esp_log_level_set("TRANSPORT", ESP_LOG_VERBOSE);
    // esp_log_level_set("outbox", ESP_LOG_VERBOSE);

    sprintf(MQTT_TOPIC_INFO, "hjy-dev/h03/%s/info/", device_attr.device_mac);
    sprintf(MQTT_TOPIC_ATTR, "hjy-dev/h03/%s/attr/", device_attr.device_mac);
    sprintf(MQTT_TOPIC_EVENT, "hjy-dev/h03/%s/event/", device_attr.device_mac);
    sprintf(MQTT_TOPIC_CTRL, "hjy-dev/h03/%s/ctrl/", device_attr.device_mac);
    sprintf(MQTT_TOPIC_FUNC, "hjy-dev/h03/%s/func/", device_attr.device_mac);
    sprintf(MQTT_TOPIC_REPORT, "hjy-dev/h03/%s/report/", device_attr.device_mac);

    ESP_LOGD(TAG,"MQTT_TOPIC_INFO = %s",MQTT_TOPIC_INFO);
    ESP_LOGD(TAG,"MQTT_TOPIC_ATTR = %s",MQTT_TOPIC_ATTR);
    ESP_LOGD(TAG,"MQTT_TOPIC_EVENT = %s",MQTT_TOPIC_EVENT);
    ESP_LOGD(TAG,"MQTT_TOPIC_FUNC = %s",MQTT_TOPIC_FUNC);
    ESP_LOGD(TAG,"MQTT_TOPIC_CTRL = %s",MQTT_TOPIC_CTRL);
    ESP_LOGD(TAG,"MQTT_TOPIC_REPORT = %s",MQTT_TOPIC_REPORT);

    mqtt_app_start();
}